package cn.iocoder.boot;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {

    // 定义人才类，用于表示候选人及其特征
    public static class Talent {
        private int[] features; // 特征向量，如技能、经验、教育背景等量化值（整数）
        private String name;    // 候选人姓名
        private int salary;     // 最低薪资要求

        // 构造方法，初始化人才对象
        public Talent(String name, int[] features, int salary) {
            this.name = name;
            this.features = features;
            this.salary = salary;
        }

        // 获取特征向量
        public int[] getFeatures() {
            return features;
        }

        // 获取候选人姓名
        public String getName() {
            return name;
        }

        // 获取最低薪资要求
        public int getSalary() {
            return salary;
        }

        // 计算两个人才特征向量之间的欧氏距离
        public double distanceTo(Talent other) {
            double sum = 0.0;
            // 遍历特征向量的每个维度，计算差值的平方和
            for (int i = 0; i < features.length; i++) {
                sum += Math.pow(features[i] - other.features[i], 2);
            }
            // 返回平方和的平方根，即欧氏距离
            return Math.sqrt(sum);
        }

        // 人才能力描述
        public String getAbilityDescription() {
            return "技能: " + features[0] + ", 经验: " + features[1] + ", 教育背景: " + features[2] + ", 最低薪资要求: " + salary;
        }
    }

    // 定义簇类，用于表示聚类过程中的一个簇
    public static class Cluster {
        private List<Talent> talents = new ArrayList<>(); // 簇中的候选人才
        private Talent centroid;                         // 簇中心

        // 获取簇中的候选人才列表
        public List<Talent> getTalents() {
            return talents;
        }

        // 获取簇中心
        public Talent getCentroid() {
            return centroid;
        }

        // 设置簇中心
        public void setCentroid(Talent centroid) {
            this.centroid = centroid;
        }

        // 添加人才到簇中
        public void addTalent(Talent talent) {
            talents.add(talent);
        }

        // 更新簇中心，通过计算簇中所有人才特征的均值来确定新的簇中心
        public void updateCentroid() {
            if (talents.isEmpty()) return;

            double[] newCentroidFeatures = new double[centroid.getFeatures().length];
            // 累加每个特征的值
            for (Talent talent : talents) {
                for (int i = 0; i < talent.getFeatures().length; i++) {
                    newCentroidFeatures[i] += talent.getFeatures()[i];
                }
            }
            // 计算每个特征的平均值，作为新簇中心的特征值
            for (int i = 0; i < newCentroidFeatures.length; i++) {
                newCentroidFeatures[i] /= talents.size();
            }
            // 创建新的簇中心对象，这里将特征值转为整数
            int[] centroidFeaturesInt = new int[newCentroidFeatures.length];
            for (int i = 0; i < newCentroidFeatures.length; i++) {
                centroidFeaturesInt[i] = (int) Math.round(newCentroidFeatures[i]);
            }
            centroid = new Talent("Centroid", centroidFeaturesInt, 0); // 聚类中心的薪资设置为0，因为它不参与聚类计算
        }
    }

    // K-means 聚类算法实现
    public static List<Cluster> kMeans(List<Talent> talents, int k, int maxIterations) {
        // 初始化簇列表
        List<Cluster> clusters = new ArrayList<>();
        Random random = new Random();
        // 随机选择初始簇中心
        for (int i = 0; i < k; i++) {
            Talent initialCentroid = new Talent("Centroid", talents.get(random.nextInt(talents.size())).getFeatures(), 0);
            Cluster cluster = new Cluster();
            cluster.setCentroid(initialCentroid);
            clusters.add(cluster);
        }

        // 聚类迭代过程
        for (int iteration = 0; iteration < maxIterations; iteration++) {
            // 清空每个簇中的人才列表，准备重新分配
            for (Cluster cluster : clusters) {
                cluster.getTalents().clear();
            }

            // 将每个人才分配到最近的簇
            for (Talent talent : talents) {
                Cluster nearestCluster = null;
                double minDistance = Double.MAX_VALUE;
                // 找到距离当前人才最近的簇中心
                for (Cluster cluster : clusters) {
                    double distance = talent.distanceTo(cluster.getCentroid());
                    if (distance < minDistance) {
                        minDistance = distance;
                        nearestCluster = cluster;
                    }
                }
                // 将人才添加到最近的簇中
                nearestCluster.addTalent(talent);
            }

            // 更新每个簇的中心
            boolean converged = true;
            for (Cluster cluster : clusters) {
                // 保存当前簇的中心和人才列表，用于判断是否收敛
                Cluster oldCluster = new Cluster();
                oldCluster.setCentroid(cluster.getCentroid());
                oldCluster.getTalents().addAll(cluster.getTalents());
                // 更新簇中心
                cluster.updateCentroid();
                // 判断簇中心是否发生变化
                if (!isSameCentroid(oldCluster.getCentroid(), cluster.getCentroid())) {
                    converged = false;
                }
            }

            // 如果所有簇中心不再变化，提前结束迭代
            if (converged) {
                break;
            }
        }

        // 返回聚类结果
        return clusters;
    }

    // 判断两个簇中心是否相同，用于判断聚类是否收敛
    private static boolean isSameCentroid(Talent centroid1, Talent centroid2) {
        if (centroid1 == null || centroid2 == null) {
            return false;
        }
        if (centroid1.getFeatures().length != centroid2.getFeatures().length) {
            return false;
        }
        // 比较每个特征值是否相同
        for (int i = 0; i < centroid1.getFeatures().length; i++) {
            if (centroid1.getFeatures()[i] != centroid2.getFeatures()[i]) {
                return false;
            }
        }
        return true;
    }

    // 基于聚类结果进行个性化推荐
    public static List<Talent> recommendTalentsForPosition(List<Cluster> clusters, Talent position) {
        List<Talent> recommendedTalents = new ArrayList<>();

        // 找到与岗位需求最相似的簇
        Cluster bestMatchCluster = null;
        double minDistance = Double.MAX_VALUE;
        for (Cluster cluster : clusters) {
            double distance = position.distanceTo(cluster.getCentroid());
            if (distance < minDistance) {
                minDistance = distance;
                bestMatchCluster = cluster;
            }
        }

        // 如果找到了匹配的簇，推荐该簇中的人才
        if (bestMatchCluster != null) {
            recommendedTalents.addAll(bestMatchCluster.getTalents());
        }

        return recommendedTalents;
    }

    // 生成模拟数据，用于测试算法
    public static List<Talent> generateSampleTalents(int numTalents) {
        List<Talent> talents = new ArrayList<>();
        Random random = new Random();
        String[] names = {"Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Helen", "Ivy", "Jack"};
        for (int i = 0; i < numTalents; i++) {
            // 随机生成经验（1 到 10 年）
            int experience = random.nextInt(10) + 1;
            // 根据经验生成技能值，经验越高，技能值越高，技能值范围：[1, 10]
            int skill = random.nextInt(experience + 1) + 1;
            if (skill > 10) {
                skill = 10;
            }
            // 随机生成教育背景值（0 到 10）
            int education = random.nextInt(11);

            // 根据技能、经验和教育背景的加权和确定薪资
            double weightedSum = skill * 0.65 + experience * 0.25 + education * 0.10;
            // 将加权和归一化到 0-10 范围，然后映射到 3000-20000 的薪资范围
            int salary = (int) (3000 + (weightedSum / 10) * 17000);
            // 确保薪资在 3000-20000 范围内
            salary = Math.max(3000, Math.min(salary, 20000));

            // 创建人才对象并添加到列表中
            talents.add(new Talent(names[i % names.length], new int[]{skill, experience, education}, salary));
        }
        return talents;
    }

    // 为岗位推荐的人才的理由以及人才的能力和对比过程
    public static void recommendTalentsWithReason(List<Cluster> clusters, Talent position, int maxSalary) {
        List<Talent> recommendedTalents = recommendTalentsForPosition(clusters, position);
        List<Talent> affordableTalents = new ArrayList<>();

        // 筛选出薪资要求在企业可支出上限内且技能值大于等于5的候选人
        for (Talent talent : recommendedTalents) {
            if (talent.getSalary() <= maxSalary && talent.getFeatures()[0] >= 5) {
                affordableTalents.add(talent);
            }
        }

        // 计算每个候选人的价值（可以根据实际业务需求定义价值计算方式）
        List<TalentValue> talentValues = new ArrayList<>();
        for (Talent talent : affordableTalents) {
            double value = talent.getFeatures()[0] * 0.5 + talent.getFeatures()[1] * 0.3 + talent.getFeatures()[2] * 0.2; // 示例价值计算方式
            talentValues.add(new TalentValue(talent, value));
        }

        // 按价值从高到低排序
        talentValues.sort((a, b) -> Double.compare(b.getValue(), a.getValue()));

        // 输出推荐结果
        System.out.println("\n为企业推荐的人才（在薪资上限内价值最高）：");
        for (int i = 0; i < Math.min(5, talentValues.size()); i++) { // 输出前5个价值最高的候选人
            TalentValue talentValue = talentValues.get(i);
            Talent talent = talentValue.getTalent();
            double matchPercentage = (1 - talent.distanceTo(position) / maxDistance(position, affordableTalents)) * 100;

            System.out.println("  排名 " + (i + 1) + "：");
            System.out.println("    候选人: " + talent.getName());
            System.out.println("    能力: " + talent.getAbilityDescription());
            System.out.println("    与岗位需求的匹配度: " + matchPercentage + "%");
            System.out.println("    价值评分: " + talentValue.getValue());

            // 分析推荐理由
            StringBuilder reason = new StringBuilder("推荐理由：");
            int[] talentFeatures = talent.getFeatures();
            int[] positionFeatures = position.getFeatures();
            boolean skillMatched = talentFeatures[0] >= positionFeatures[0];
            boolean experienceMatched = talentFeatures[1] >= positionFeatures[1];
            boolean educationMatched = talentFeatures[2] >= positionFeatures[2];

            // 技能匹配分析
            reason.append("技能匹配度：");
            if (skillMatched) {
                reason.append("该候选人的技能水平达到或超过了岗位要求，能够熟练应对岗位所需的技术挑战。");
            } else {
                reason.append("该候选人的技能水平与岗位要求有差距，但仍具备一定的相关能力，可能需要进一步培训或学习。");
            }

            // 经验匹配分析
            reason.append(" 经验匹配度：");
            if (experienceMatched) {
                reason.append("经验丰富，能够快速适应岗位并做出贡献。");
            } else {
                reason.append("经验稍有不足，但具有较强的学习能力和成长潜力。");
            }

            // 教育背景匹配分析
            reason.append(" 教育背景匹配度：");
            if (educationMatched) {
                reason.append("教育背景符合岗位要求，表明候选人具备扎实的理论基础。");
            } else {
                reason.append("教育背景与岗位要求略有差距，但通过实际工作经验弥补了这一不足。");
            }

            // 薪资匹配分析
            reason.append(" 薪资匹配度：");
            if (talent.getSalary() <= maxSalary * 0.8) {
                reason.append("候选人期望薪资低于企业可支出上限，具有较高的性价比。");
            } else {
                reason.append("候选人期望薪资接近企业可支出上限，需综合评估其技能和经验是否值得该薪资水平。");
            }

            System.out.println("    " + reason.toString());
        }
    }

    // 计算岗位需求与推荐人才之间的最大距离，用于归一化匹配度
    private static double maxDistance(Talent position, List<Talent> recommendedTalents) {
        double maxDistance = 0.0;
        for (Talent talent : recommendedTalents) {
            double distance = talent.distanceTo(position);
            if (distance > maxDistance) {
                maxDistance = distance;
            }
        }
        return maxDistance;
    }

    // 定义一个辅助类，用于存储候选人的价值信息
    public static class TalentValue {
        private Talent talent;
        private double value;

        public TalentValue(Talent talent, double value) {
            this.talent = talent;
            this.value = value;
        }

        public Talent getTalent() {
            return talent;
        }

        public double getValue() {
            return value;
        }
    }

    // 增加一个设置企业要求的函数
    public static void setCompanyRequirements(Talent position, int maxSalary) {
        System.out.println("\n企业要求：");
        System.out.println("  技能要求：至少 " + position.getFeatures()[0] + " 分");
        System.out.println("  经验要求：至少 " + position.getFeatures()[1] + " 年");
        System.out.println("  教育背景要求：至少 " + position.getFeatures()[2] + " 分");
        System.out.println("  可支出薪水上限：最多 " + maxSalary + " 元");
        System.out.println("  能力要求：技能值必须大于等于5");
    }

    public static void main(String[] args) {
        // 生成模拟人才数据
        List<Talent> talents = generateSampleTalents(20);

        // 使用 K-means 聚类算法进行聚类
        int k = 3; // 假设分为 3 类人才
        int maxIterations = 100;
        List<Cluster> clusters = kMeans(talents, k, maxIterations);

        // 输出聚类结果
        System.out.println("聚类结果：");
        for (int i = 0; i < clusters.size(); i++) {
            Cluster cluster = clusters.get(i);
            System.out.println("簇 " + (i + 1) + "（中心点： " + cluster.getCentroid().getName() + "）：");
            for (Talent talent : cluster.getTalents()) {
                System.out.println("  " + talent.getName() + " - 能力: " + talent.getAbilityDescription());
            }
        }

        // 模拟岗位人才推荐
        // 假设有一个岗位需求，其特征向量为 [8, 6, 7]（技能、经验、教育背景）
        Talent position = new Talent("Software Engineer Position", new int[]{8, 6, 7}, 0); // 岗位对象的薪资设为0，不参与计算

        // 设置企业可支出薪水上限
        int maxSalary = 20000;

        // 输出企业的要求
        setCompanyRequirements(position, maxSalary);

        recommendTalentsWithReason(clusters, position, maxSalary);
    }
}